#pragma once

#ifndef _CLIENT_SYSTEM_CLIENT_SYSTEM_H_
#define _CLIENT_SYSTEM_CLIENT_SYSTEM_H_

#include "Poco\SharedPtr.h"
#include "Poco\BasicEvent.h"
#include "Poco\Event.h"
#include "Poco\Net\SocketAddress.h"

#include "Core\StanzaHandler.h"
#include "Core\Packages.h"
#include "Core\Definations.h"
#include "Core\SafeMutex.h"
#include "Core\QuestionLibrary.h"
#include "Core\AnswerDatabase.h"

namespace ExamSystem
{
    using Poco::SharedPtr;
    using Poco::BasicEvent;
    using Poco::Event;
    using Poco::Net::SocketAddress;

    class CExamClientConnection;


namespace Client
{
    class CServerHandler;

    /**************************************************************************\
     Component:     CClientSystem
     Description:
        This class is the internal core of the exam client. It manage the 
        connections to (root) servers, as well as providing several interfaces
        for UI part
    \**************************************************************************/
    class CClientSystem
    {
        friend class CServerHandler;
    public:
        CClientSystem(void);
        ~CClientSystem(void);

    public:
        void SetRootServerAddress(const std::string &host, Poco::UInt16 port);
        void SetRootServerAddress(const SocketAddress &address);

        bool Initialize(void);
        bool IsInitialized(void) const;
        void Finalize(void);

        // these two funciton only sends login command
        void StudentLogin(const std::string &name, const std::string &number);
        void InvigilatorLogin(const std::string &user, const std::string &password);

        const StudentInfo &GetLoggedInStudent(void) const;

        // block and wait for the result for login
        CLoginResultPackage::LoginResult WaitForLoginResult(void);
        CLoginResultPackage::LoginResult GetLoginResult(void);

        // ask for question and criterion
        void RequireQuestions(void);
        void RequireCriterion(void);
        void WaitForQuestionCriterion(void);

        // score the answers
        struct ScoreParameters
        {
            std::string officeParser;
            std::string htmlParser;
            std::string codeChecker;
        };
        void ScoreAnswers(const ScoreParameters &param);

        // database
        CQuestionLibrary &GetQuestionLibrary(void);
        CAnswerDatabase &GetAnswerDatabase(void);
        void SendAnswerDatabase(void);
        void WaitForAnswerDatabaseSent(void);

        // return whether the exam has started
        bool IsExamStarted(void) const;
        bool IsExamFinished(void) const;

        // respond to exam order
        void RespondToOrder(COrderPackage &order);

        // respond to exam messages
        void RespondToMessage(CMessagePackage &message);

        // set up this server system according to received exam information
        void SetExamInformation(const ExamInformation &information);

        // set the examination folder
        // all questions ans answers will be put in it
        // the default folder is c:\exam
        void SetExamFolder(const std::string &folder);
        const std::string &GetExamFolder(void) const;

        void SetExamTempFolder(const std::string &folder);
        const std::string &GetExamTempFolder(void) const;

        // rest time
        unsigned int GetExamRestTime(void);

    protected:
        // event handlers      
        void OnServerConnectionClosed(const void* pSender, int &id);
        void OnServerConnectionDisconnected(const void* pSender, int &id);
        void OnServerConnectionReconnected(const void *pSender, int &id);
        void OnServerConnectionRedirected(const void *pSender, SocketAddress &targetAddress);
        void OnServerConnectionVersionCheckFailed(const void *pSender, void *const& pConnection);
        void OnServerConnectionReady(const void *pSender, void *const& pConnection);

        void OnLoginResult(const void *pSender, CLoginResultPackage &result);


    protected:
        bool ConnectServer(void);
        bool CreateHandler(void);
        void DisconnectServer(void);
        void SendConnectionInfo(void);
        bool InitializeExamFolder(void);

        void ReceiveQuestion(CQuestionPackage &question);
        void ReceiveCriterion(CCriterionPackage &criterion);

        void AnswerDatabaseSent(void);

    public:
        // events
        BasicEvent<CClientSystem *const> eventServerConnectionDisconnected;
        BasicEvent<CClientSystem *const> eventServerConnectionClosed;

        BasicEvent<int> eventExamStarted;
        BasicEvent<int> eventExamStopped;

        BasicEvent<std::string> eventExamMessage;
        BasicEvent<std::string> eventExamAlert;

    protected:
        // the connection to the servers
        SharedPtr<CExamClientConnection> m_serverConnection;
        SharedPtr<CExamClientConnection> m_zombieServerConnection;
        // handler for the connection to server
        SharedPtr<CServerHandler> m_serverConnectionHandler;
        // initialization lock
        Event m_synInitialization;

        // connecting address
        SocketAddress m_rootServerAddress;

        ExamInformation m_examInformation;

        // this flag identifies whether the connection has been redirected
        bool m_flagConnectionRedirected;

        // this flag identifies whether the connection is voluntarily closed
        bool m_flagValuntaryClosure;
        
        // login
        typedef StudentInfo StudentInfo;
        StudentInfo m_loginStudent;
        CLoginResultPackage::LoginResult m_loginResult;
        Event m_synLogin;

        // databases
        CQuestionLibrary m_questionLibrary;
        CAnswerDatabase m_answerDatabase;
        std::string m_questionLibraryFile;
        std::string m_answerDatabaseFile;
        CSafeMutex m_questionLibraryLock;
        CSafeMutex m_answerDatabaseLock;
        bool m_flagAnswerDatabaseSent;

        // question and criterion
        Event m_synQuestionCriterion;
        // answer database
        Event m_synSendingAnswerDatabase;

        bool m_flagQuestionsReceived;

        // the examination folder, all questions ans answers will be put in this folder
        std::string m_examFolder;

        // temp folder
        std::string m_examTempFolder;

        // initialize flag
        bool m_flagInitialized;
    };

    // the only instance
    extern CClientSystem theClientSystem;
}
}
#endif